#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <openssl/evp.h>
#include <openssl/pem.h>
#include <openssl/err.h>

void handle_errors() {
    ERR_print_errors_fp(stderr);
    abort();
}

int verify_signature(const char *public_key_file, const char *input_file, const char *signature_file) {
    // 打开输入文件
    FILE *fp = fopen(input_file, "rb");
    if (!fp) {
        perror("Unable to open input file");
        return EXIT_FAILURE;
    }

    // 读取文件内容
    fseek(fp, 0, SEEK_END);
    long file_size = ftell(fp);
    fseek(fp, 0, SEEK_SET);
    unsigned char *data = malloc(file_size);
    if (!data) {
        perror("Unable to allocate memory");
        fclose(fp);
        return EXIT_FAILURE;
    }
    fread(data, 1, file_size, fp);
    fclose(fp);

    // 打开签名文件
    FILE *sig_fp = fopen(signature_file, "rb");
    if (!sig_fp) {
        perror("Unable to open signature file");
        free(data);
        return EXIT_FAILURE;
    }

    // 读取签名
    fseek(sig_fp, 0, SEEK_END);
    long sig_size = ftell(sig_fp);
    fseek(sig_fp, 0, SEEK_SET);
    unsigned char *signature = malloc(sig_size);
    if (!signature) {
        perror("Unable to allocate memory for signature");
        fclose(sig_fp);
        free(data);
        return EXIT_FAILURE;
    }
    fread(signature, 1, sig_size, sig_fp);
    fclose(sig_fp);

    // 读取公钥
    FILE *key_fp = fopen(public_key_file, "r");
    if (!key_fp) {
        perror("Unable to open public key file");
        free(signature);
        free(data);
        return EXIT_FAILURE;
    }

    // 从 PEM 文件读取 SM2 公钥
    EVP_PKEY *pkey = PEM_read_PUBKEY(key_fp, NULL, NULL, NULL);
    fclose(key_fp);
    if (!pkey) {
        handle_errors();
    }

    // 创建验证上下文
    EVP_MD_CTX *ctx = EVP_MD_CTX_new();
    if (!ctx) {
        handle_errors();
    }

    // 初始化验证操作
    if (EVP_DigestVerifyInit(ctx, NULL, EVP_sm3(), NULL, pkey) != 1) {
        handle_errors();
    }

    // 提供要验证的数据
    if (EVP_DigestVerifyUpdate(ctx, data, file_size) != 1) {
        handle_errors();
    }

    // 验证签名
    int ret = EVP_DigestVerifyFinal(ctx, signature, sig_size);
    if (ret == 1) {
        printf("Signature verification successful!\n");
    } else {
        printf("Signature verification failed!\n");
    }

    // 清理资源
    EVP_MD_CTX_free(ctx);
    EVP_PKEY_free(pkey);
    free(signature);
    free(data);

    return ret == 1 ? EXIT_SUCCESS : EXIT_FAILURE;
}

int main(int argc, char **argv) {
    if (argc != 4) {
        fprintf(stderr, "Usage: %s <public_key.pem> <input_file> <signature_file>\n", argv[0]);
        return EXIT_FAILURE;
    }

    return verify_signature(argv[1], argv[2], argv[3]);
}
